package com.neucloud.da.edgeside.business;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.amazonaws.util.IOUtils;
import com.neucloud.da.edgeside.model.constant.EngineCacheConstant;
import com.neucloud.da.edgeside.model.constant.EngineConstant;
import com.neucloud.da.edgeside.model.entity.InfereceStatusEntity;
import com.neucloud.da.edgeside.model.entity.InferenceEntity;
import com.neucloud.da.edgeside.model.entity.ModelVersionEntity;
import com.neucloud.da.edgeside.model.enums.InferenceStatusEnum;
import com.neucloud.da.edgeside.model.enums.InferenceTypeEnum;
import com.neucloud.da.edgeside.model.exception.BusinessException;
import com.neucloud.da.edgeside.model.service.IStorageService;
import com.neucloud.da.edgeside.model.utils.UUIDUtil;
import com.neucloud.da.edgeside.service.IInferenceService;
import com.neucloud.da.edgeside.utils.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;

@Slf4j
@Service
public class IsvcOnlineBusiness extends ReqBaseBusiness{

    @Value("${model.s3.bucket}")
    private String modelBucket;
    @Value("${online.inference.url}")
    String onlineInferenceUrl;
    @Value("${k8s.da.gateway.url}")
    String daGatewayUrl;
    @Value("${k8s.da.model.url}")
    String daModelUrl;
    @Value("${online.inference.url}")
    String inferenceBaseUrl;
    @Autowired
    IStorageService storageService;
    @Autowired
    IInferenceService inferenceService;
    @Resource(name = "commonTemplate")
    RestTemplate restTemplate;
    @Autowired
    RedisTemplate redisTemplate;


    public String createIsvcOnline(String version,MultipartFile file) {
        String targetProcFile = "";
        if (file == null) {
            throw new BusinessException("下方文件不能为空");
        }
        if (StrUtil.isEmpty(version) || StrUtil.isEmpty(version)) {
            throw new BusinessException("模型版本不能为空，请检查参数");
        }

        HttpHeaders headers = new HttpHeaders();
        HttpEntity httpEntity = new HttpEntity<>(headers);
        StringBuffer modelVersionUrl = new StringBuffer();
        modelVersionUrl.append(daGatewayUrl);
        modelVersionUrl.append(daModelUrl);
        modelVersionUrl.append("?versionId=");
        modelVersionUrl.append(version);
        ModelVersionEntity modelVersionEntity = null;
        try {
            ResponseEntity<ModelVersionEntity> resp = restTemplate.exchange(URI.create(modelVersionUrl.toString()), HttpMethod.GET,httpEntity,ModelVersionEntity.class);
            modelVersionEntity = resp.getBody();
        } catch (RestClientException e) {
            e.printStackTrace();
            throw new BusinessException("未获取模型版本信息。");
        }
        //解压文件
        try {
            InputStream inputStream = file.getInputStream();
            ArchiveInputStream archiveInputStream = new ZipArchiveInputStream(inputStream);
            ArchiveEntry entry = null;
            int cpu = 0;
            int gpu = 0;
            int memory = 0;
            while ((entry = archiveInputStream.getNextEntry()) != null) {
                String fileName = entry.getName();
                if(fileName.equals("config.ini")){
                    String fileContent = IOUtils.toString(archiveInputStream);
                    try{
                        JSONObject jsonContent = JSONObject.parseObject(fileContent);
                        if ((!jsonContent.keySet().contains("inputs")) || (!jsonContent.keySet().contains("outputs"))) {
                            throw new BusinessException("config.ini配置文件必须有inputs和outputs字段。");
                        }
                        if ((!jsonContent.keySet().contains("cpu"))) {
                            throw new BusinessException("config.ini配置文件必须有cpu字段。");
                        }else {
                            cpu = jsonContent.getIntValue("cpu");
                        }
                        if ((!jsonContent.keySet().contains("memory"))) {
                            throw new BusinessException("config.ini配置文件必须有memory字段。");
                        }else {
                            memory = jsonContent.getIntValue("memory");
                        }
                        if ((jsonContent.keySet().contains("gpu"))) {
                            gpu = jsonContent.getIntValue("gpu");
                        }
                        if ((!jsonContent.keySet().contains("service"))) {
                            throw new BusinessException("config.ini配置文件必须有gpu字段。");
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        String message = e.getMessage();
                        if(StrUtil.isNotEmpty(message)){
                            throw new BusinessException("配置文件解析失败:" + message);
                        }else {
                            throw new BusinessException("配置文件解析失败:" + e.toString());
                        }
                    }
                }
                //处理推理服务前后处理
                if(fileName.equals("inference_service.py")){
                    if(FileUtil.checkFileSize(entry.getSize(),20)){
                        throw new BusinessException("上传的文件模板大小不能超过20M。");
                    }
                    targetProcFile = UUIDUtil.getId() + InferenceTypeEnum.ONLINE.suffix;
                    String filePath = String.format(EngineConstant.MODE_SCRIPT_PATH, modelVersionEntity.getProjectId(), version, targetProcFile);
                    storageService.uploadFile(modelBucket,filePath,archiveInputStream,entry.getSize());
                }
            }
            archiveInputStream.close();

            //创建推理服务任务
            String onlineServiceId = inferenceService.getOnlineServiceId();
            InferenceEntity inferenceEntity = new InferenceEntity();
            inferenceEntity.setCpu(cpu);
            inferenceEntity.setMemory(memory);
            inferenceEntity.setGpu(gpu);
            inferenceEntity.setName(InferenceTypeEnum.ONLINE.prefix + onlineServiceId);
            inferenceEntity.setType(InferenceTypeEnum.ONLINE.code);
            inferenceEntity.setInferenceId(inferenceService.getOnlineServiceId());
            inferenceEntity.setProjectId(modelVersionEntity.getProjectId());
            inferenceEntity.setTargetProcFile(targetProcFile);
            inferenceService.createOnline(inferenceEntity,modelVersionEntity);
            return onlineServiceId;
        } catch (IOException e) {
            e.printStackTrace();
            throw new BusinessException("配置文件解析失败:" + e.toString());
        }
    }

    public InfereceStatusEntity getIsvcOnlineStatus(String isvcOnlineId) {
        InfereceStatusEntity statusEntity = inferenceService.queryOnlineStatus(isvcOnlineId);
        switch (InferenceStatusEnum.getByCode(statusEntity.getStatus())){
            case RUNING:
                statusEntity.setApiUrl(String.format(onlineInferenceUrl,isvcOnlineId));
                break;
        }
        return statusEntity;
    }

    public ResponseEntity onlineApi(String isvcOnlineId){
        String reqUrl = (String) redisTemplate.opsForValue().get(String.format(EngineCacheConstant.ISVC_EXIST,isvcOnlineId));
        if(StrUtil.isEmpty(reqUrl)){
            InfereceStatusEntity onlineStatus = inferenceService.queryOnlineStatus(isvcOnlineId);
            switch (InferenceStatusEnum.getByCode(onlineStatus.getStatus())){
                case RUNING:
                    reqUrl = onlineStatus.getApiUrl();
                    redisTemplate.opsForValue().set(String.format(EngineCacheConstant.ISVC_EXIST,isvcOnlineId), reqUrl);
                    break;
                case STOP:
                    throw new BusinessException("服务已停止");
                case STARTING:
                    throw new BusinessException("服务正在启动中,请稍后再试");
                case FAIL:
                    throw new BusinessException("服务异常,原因:"+onlineStatus.getFailMsg());
            }
        }
        try {
            ResponseEntity resp = reqeustForward(reqUrl, "", getReqeustParams(), getRequestBody(),getReqeustMethod());
            return resp;
        } catch (Exception exception) {
            log.error("API调试异常:{}", exception);
            throw new BusinessException(exception.getMessage());
        }
    }

    public Boolean stopOnline(String isvcOnlineId) {
        inferenceService.removeIsvcOnline(isvcOnlineId);
        return true;
    }

    public Boolean delOnline(String isvcOnlineId) {
        inferenceService.removeIsvcOnline(isvcOnlineId);
        return true;
    }





}
